package com.example.opengl.render.video

import android.graphics.SurfaceTexture
import android.opengl.GLES11Ext
import android.opengl.GLES20
import android.opengl.GLES30
import android.opengl.Matrix
import android.util.Log
import com.example.opengl.render.base.IDrawer
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.FloatBuffer

/**
 * 自定义视频渲染器
 */
class VideoDrawer : IDrawer {

    /**
     * 定义顶点坐标
     */
    private val mVertexCoors = floatArrayOf(
        -1f, -1f,
        1f, -1f,
        -1f, 1f,
        1f, 1f
    )

    /**
     * 定义纹理坐标
     */
    private val mTextureCoors = floatArrayOf(
        0f, 1f,
        1f, 1f,
        0f, 0f,
        1f, 0f
    )

    /**
     * 窗口大小
     */
    private var mWorldWidth: Int = -1
    private var mWorldHeight = -1

    /**
     * 视频画面大小
     */
    private var mVideoWidth = -1
    private var mVideoHeight = -1

    //纹理id
    private var mTextureId: Int = -1
    private var mSurfaceTexture: SurfaceTexture? = null

    //OpenGL程序ID
    private var mProgram: Int = -1


    //矩阵变换接收者
    private var mVertexMatrixHandler: Int = -1
    // 顶点坐标接收者
    private var mVertexPosHandler: Int = -1

    // 纹理坐标接收者
    private var mTexturePosHandler: Int = -1

    // 纹理接收者
    private var mTextureHandler: Int = -1

    private lateinit var mVertexBuffer: FloatBuffer
    private lateinit var mTextureBuffer: FloatBuffer
    private var mMatrix: FloatArray? = null



    private var mSurface: ((SurfaceTexture) -> Unit)? = null


    init {
        initPosition()
    }

    private fun initPosition() {
        val bb = ByteBuffer.allocateDirect(mVertexCoors.size * 4)
        bb.order(ByteOrder.nativeOrder())

        //将坐标数据转换为FloatBuffer
        mVertexBuffer = bb.asFloatBuffer()
        mVertexBuffer.put(mVertexCoors)
        mVertexBuffer.position(0)

        val cc = ByteBuffer.allocateDirect(mTextureCoors.size * 4)
        cc.order(ByteOrder.nativeOrder())
        mTextureBuffer = cc.asFloatBuffer()
        mTextureBuffer.put(mTextureCoors)
        mTextureBuffer.position(0)
    }


    override fun draw() {
        //有效纹理id
        if (mTextureId != -1) {
            //设置显示窗口
            initWindow()
            //创建，编译并启动OpenGl着色器
            createGLPrg()
            //激活并绑定纹理单元
            activateTexture()
            //绑定视图到纹理单元
            updateTexture()
            //开始渲染绘制
            doDraw()
        }
    }

    private var mWidthRatio = 1f
    private var mHeightRatio = 1f


    /**
     *  * @param m returns the result 正交投影矩阵
     * @param mOffset 偏移量，默认为 0 ,不偏移
     * @param left 左平面距离
     * @param right 右平面距离
     * @param bottom 下平面距离
     * @param top 上平面距离
     * @param near 近平面距离
     * @param far 远平面距离
     * public static void orthoM(float[] m, int mOffset,
     * float left, float right, float bottom, float top,
     * float near, float far)
     *
     *
     *
     *
     */


    private fun initWindow() {
        if (mMatrix != null) return
        if (mVideoWidth != -1 && mVideoHeight != -1 &&
            mWorldWidth != -1 && mWorldHeight != -1
        ) {
            mMatrix = FloatArray(16)
            val prjMatrix = FloatArray(16)
            val originRatio = mVideoWidth / mVideoHeight.toFloat()
            val worldRatio = mWorldWidth / mWorldHeight.toFloat()
            if (mWorldWidth > mWorldHeight) {
                if (originRatio > worldRatio) {
                    mHeightRatio = originRatio / worldRatio
                    Matrix.orthoM(
                        prjMatrix, 0,
                        -mWidthRatio, mWidthRatio,
                        -mHeightRatio, mHeightRatio,
                        3f, 5f
                    )
                } else {// 原始比例小于窗口比例，缩放高度度会导致高度超出，因此，高度以窗口为准，缩放宽度
                    mWidthRatio = worldRatio / originRatio
                    Matrix.orthoM(
                        prjMatrix, 0,
                        -mWidthRatio, mWidthRatio,
                        -mHeightRatio, mHeightRatio,
                        3f, 5f
                    )
                }
            } else {
                if (originRatio > worldRatio) {
                    mHeightRatio = originRatio / worldRatio
                    Matrix.orthoM(
                        prjMatrix, 0,
                        -mWidthRatio, mWidthRatio,
                        -mHeightRatio, mHeightRatio,
                        3f, 5f
                    )
                } else {// 原始比例小于窗口比例，缩放高度会导致高度超出，因此，高度以窗口为准，缩放宽度
                    mWidthRatio = worldRatio / originRatio
                    Matrix.orthoM(
                        prjMatrix, 0,
                        -mWidthRatio, mWidthRatio,
                        -mHeightRatio, mHeightRatio,
                        3f, 5f
                    )
                }
            }


            //设置相机位置
            val viewMatrix = FloatArray(16)
            Matrix.setLookAtM(
                viewMatrix, 0,
                0f, 0f, 5.0f,
                0f, 0f, 0f,
                0f, 1.0f, 0f
            )
            //计算变换矩阵
            Matrix.multiplyMM(mMatrix, 0, prjMatrix, 0, viewMatrix, 0)
        }
    }

    /**
     * 开始渲染
     */
    private fun doDraw() {
        Log.i("TAGdraw", "doDraw: ")
        //启用顶点句柄
        GLES20.glEnableVertexAttribArray(mVertexPosHandler)
        GLES20.glEnableVertexAttribArray(mTexturePosHandler)
        GLES20.glUniformMatrix4fv(mVertexMatrixHandler, 1, false, mMatrix, 0)
        //设置着色器参数,第二个参数表示一个顶点包含的数据数量
        GLES20.glVertexAttribPointer(mVertexPosHandler, 2, GLES20.GL_FLOAT, false, 0, mVertexBuffer)
        GLES20.glVertexAttribPointer(mTexturePosHandler, 2, GLES20.GL_FLOAT, false, 0, mTextureBuffer)
        //开始绘制
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4)
    }

    /**
     * 绑定视图到纹理单元
     */
    private fun updateTexture() {
        mSurfaceTexture?.updateTexImage()
    }

    /**
     * 激活纹理并绑定纹理单元
     */
    private fun activateTexture() {
        //激活指定纹理
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0)
        //绑定纹理id到纹理单元
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureId)
        //将激活的纹理单元传递给着色器
        GLES20.glUniform1i(mTextureHandler, 0)
        //配置边缘过渡参数
        GLES20.glTexParameterf(
            GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
            GLES20.GL_TEXTURE_MIN_FILTER,
            GLES20.GL_LINEAR.toFloat()
        )
        GLES20.glTexParameterf(
            GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
            GLES20.GL_TEXTURE_MAG_FILTER,
            GLES20.GL_LINEAR.toFloat()
        )
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE)
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE)
    }

    private fun createGLPrg() {
        if (mProgram == -1) {
            //加载顶点着色器
            val vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, getVertexShader())
            //加载片段着色器
            val fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, getFragmentShader())

            //创建OpenGL程序，必须要在OpenGl渲染线程中创建，否则不能渲染
            mProgram = GLES20.glCreateProgram()
            //将顶点着色器加到程序中
            GLES20.glAttachShader(mProgram, vertexShader)
            //将片段着色器加到程序中
            GLES20.glAttachShader(mProgram, fragmentShader)
            //连接着色器程序
            GLES20.glLinkProgram(mProgram)

            //glGetAttribLocation ---- 返回 attribute 属性变量的 location（索引值）
            mVertexMatrixHandler = GLES20.glGetUniformLocation(mProgram, "uMatrix")
            mVertexPosHandler = GLES20.glGetAttribLocation(mProgram, "aPosition")

            mTextureHandler = GLES20.glGetUniformLocation(mProgram, "uTexture")
            mTexturePosHandler = GLES20.glGetAttribLocation(mProgram, "aCoordinate")
        }
        //保存程序
        GLES20.glUseProgram(mProgram)

    }

    /**
     *片段着色器程序
     */
    private fun getFragmentShader(): String {
        //一定要加换行"\n"，否则会和下一行的precision混在一起，导致编译出错 ,
        return "#extension GL_OES_EGL_image_external : require\n" +
                "precision mediump float;" +
                "varying vec2 vCoordinate;" +
                "uniform samplerExternalOES uTexture;" +
                "void main() {" +
                "  gl_FragColor=texture2D(uTexture, vCoordinate);" +
                "}"
    }

    /**
     * 顶点着色器程序
     */
    private fun getVertexShader(): String {
        return "attribute vec4 aPosition;" +
                "attribute vec2 aCoordinate;" +
                "varying vec2 vCoordinate;" +
                "uniform mat4 uMatrix;" +
                "void main() {" +
                "    gl_Position = uMatrix*aPosition;" +
                "    vCoordinate = aCoordinate;" +
                "}"
    }


    /**
     * 加载顶点着色器
     */
    private fun loadShader(type: Int, shaderCode: String): Int {
        //根据type类型创建顶点着色器或片元着色器
        val shader = GLES20.glCreateShader(type)
        //加载着色器资源
        GLES20.glShaderSource(shader, shaderCode)
        //编译着色器
        GLES20.glCompileShader(shader)
        return shader
    }


    //设置纹理id
    override fun setTextureID(id: Int) {
        mTextureId = id
        mSurfaceTexture = SurfaceTexture(id)
        mSurface?.invoke(mSurfaceTexture!!)

    }

    override fun getSurfaceTexture(cb: (surfaceTexture: SurfaceTexture) -> Unit) {
        this.mSurface = cb
    }

    /**
     * 窗口大小
     */
    override fun setWorldSize(worldW: Int, worldH: Int) {
        mWorldWidth = worldW
        mWorldHeight = worldH
    }

    override fun setVideoSize(videoW: Int, videoH: Int) {
        mVideoHeight = videoH
        mVideoWidth = videoW
    }

    override fun release() {
        //禁用通用顶点属性数组
        GLES20.glDisableVertexAttribArray(mVertexPosHandler)
        GLES20.glDisableVertexAttribArray(mTexturePosHandler)
        //绑定纹理
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0)
        //释放纹理对象
        GLES20.glDeleteTextures(1, intArrayOf(mTextureId), 0)
        //删除程序对象
        GLES20.glDeleteProgram(mProgram)


    }


}